#include <wels/codec_api.h>
#include <stdio.h>
#include <string.h>
#include <opencv2/opencv.hpp>
#include <unistd.h>

using namespace cv;

//#include "yuvchang.h"
extern int Table_fv1[];
extern int Table_fv2[];
extern int Table_fu1[];
extern int Table_fu2[];

// 10k read buffer, increase it if not enough
#define MAX_READ_SIZE (1024 * 10)

//read buffer from file
int readData(FILE *stin, unsigned char *data)
{
	unsigned char b;
	int len = 0;
	int zeros = 0;
	for (;;) {
		if (fread(&b, 1, 1, stin) <= 0)
			break;
		data[len] = b;
		++len;
		if (len <= 4)
			continue;

		if (zeros < 3)
			zeros = b != 0 ? 0 : zeros + 1;
		else {
			if (b == 1) {
				fseek(stin, -4, SEEK_CUR);
				len -= 4;
				break;
			} else if (b == 0) {
				zeros = 3;
			} else
				zeros = 0;
		}
	}
	return len;
}

//display decode image
void imgShow(unsigned char **pData, SSysMEMBuffer smembuf)
{
	/*
	 * transform to rgb and show
	 */
	int width = smembuf.iWidth;
	int height = smembuf.iHeight;
	int *stride = smembuf.iStride;
	unsigned char *y = pData[0];
	unsigned char *u = pData[1];
	unsigned char *v = pData[2];

	Mat img(height, width, CV_8UC3);
	Mat yuvimg(height + height/2, width, CV_8UC1);
	unsigned char *yuvdatay = yuvimg.data;
	unsigned char *yuvdatau = yuvdatay + width * height;
	unsigned char *yuvdatav = yuvdatau + (width * height >> 1);
	unsigned char *imgdata = img.data;

	for (int i = 0; i < height; ++i) {
		for (int j = 0; j < width; ++j) {
/*
			*(imgdata++) = y[j] + 1.732446 * (u[j / 2] - 128);
			*(imgdata++) = y[j] - 0.698001 * (u[j / 2] - 128) - 0.703125 * (v[j / 2] - 128);
			*(imgdata++) = y[j] + 1.370705 * (v[j / 2] - 128);
*/
			*(imgdata++) = y[j] + Table_fu2[u[j / 2]];
			*(imgdata++) = y[j] - Table_fu1[u[j / 2]] - Table_fv2[v[j / 2]];
			*(imgdata++) = y[j] + Table_fv1[v[j / 2]];
		}
		y += stride[0];
		if (i & 1) {
			u += stride[1];
			v += stride[1];
		}
	}
	imshow("show", img);
	waitKey(40);
}

int
main(int argc, char *argv[])
{
	int iSize;
	int iRet;
	FILE *fin = fopen(argv[1], "rb");
	ISVCDecoder *pSvcDecoder;
	unsigned char *buf = new unsigned char[MAX_READ_SIZE];
	
	unsigned char *pData[3] = {NULL};
	SBufferInfo sDstBufInfo;
	memset(&sDstBufInfo, 0, sizeof(sDstBufInfo));

	if (WelsCreateDecoder(&pSvcDecoder) != 0) {
		fprintf(stderr, "error create decoder\n");
		return -1;
	}
	
	SDecodingParam sDecParam = {0};
	sDecParam.uiTargetDqLayer = UCHAR_MAX;
	sDecParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
//	sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
	sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
//	sDecParam.bParseOnly = true;

	pSvcDecoder->Initialize(&sDecParam);
	int level = 2;
	pSvcDecoder->SetOption (DECODER_OPTION_TRACE_LEVEL, &level);

	for (; (iSize = readData(fin, buf)) > 0;) {
		memset(&sDstBufInfo, 0, sizeof(sDstBufInfo));
		iRet = pSvcDecoder->DecodeFrame2(buf, iSize, pData, &sDstBufInfo);
		if (iRet != 0)
			return (-1);
		if (sDstBufInfo.iBufferStatus == 1) {
show:
			imgShow(pData, sDstBufInfo.UsrData.sSystemBuffer);
		}

		iRet = pSvcDecoder->DecodeFrame2(NULL, 0, pData, &sDstBufInfo);
		if (iRet == 0 && sDstBufInfo.iBufferStatus == 1)
			goto show;
	}

	pSvcDecoder->Uninitialize();
	WelsDestroyDecoder(pSvcDecoder);
	return 0;
}
